package net.sourceforge.cath.aspects.alsb;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;

import net.sourceforge.cath.context.Context;
import net.sourceforge.cath.context.GlobalTransaction;
import net.sourceforge.cath.logging.Logger;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;

import com.bea.wli.sb.context.MessageContext;
import com.bea.wli.sb.pipeline.RouterContext;
//import com.bea.wli.sb.pipeline.MessageContext;
import com.bea.wli.sb.pipeline.InternalPipelineContext;

public abstract aspect AlsbTracerAspectStarter  {
	pointcut scope();

	Logger logger = Logger.getInstance();
	ThreadMXBean threadMxBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();

	String getThreadName(){
		return Thread.currentThread().getName();
	}
	long startTime;

	before():scope(){
	
		String methodName = thisJoinPoint.getSignature().getName();
		// create global transaction context and deposit it to ThreadLocal
		GlobalTransaction gtx =  new GlobalTransaction();
	
		// create global transaction id
		StringBuffer txId = new StringBuffer();
		txId.append("SB");
		txId.append(System.nanoTime());
			
		// in case of Request append "REQ" Post fix
		// case of response append "RESP" post fix
		if(methodName.equals("processRequest")){
			txId.append("-REQ");
			GlobalTransaction inflightGtx = Context.peekGlobalTransaction();
			if(inflightGtx != null) gtx.setRelationship("PARENT_GTX:"+inflightGtx.getId());
			else gtx.setRelationship(null);
		}else{
			txId.append("-RESP");
		}
		gtx.setId(txId.toString());
		
		// get ALSB proxy name
		String proxyName = this.getServiceName(thisJoinPoint);
		gtx.setInitiator(proxyName);

		gtx.setThread(getThreadName() );
		gtx.setStartTime(System.currentTimeMillis());
		gtx.setStartCpuTime(threadMxBean.getCurrentThreadCpuTime());
				
		Context.pushGlobalTransaction(gtx);

		logger.startGlobalTransaction(gtx);

	}
	
	after():scope(){
		// flush log

		GlobalTransaction gtx = Context.popGlobalTransaction();
		if(gtx == null){
			logger.error("Global transaction id is null");
			return;
		}
		gtx.setEndTime(System.currentTimeMillis());
		gtx.setEndCpuTime(threadMxBean.getCurrentThreadCpuTime());
		logger.endGlobalTransaction(gtx);
		String methodName = thisJoinPoint.getSignature().getName();

		logger.log("MAP:"+gtx.getId()+" MSGID:"+getMessageId(thisJoinPoint));
		
		logger.flush();
		//logger.debug("finishing global transaction "+gtx.getId());

		//printParameters(thisJoinPoint);
	}
	
	public String getMessageId(JoinPoint point){
		String messageId = null;
		
		Object[] args = point.getArgs();
		Class[] types = ( (CodeSignature)point.getSignature()).getParameterTypes();
		
		for(int i=0;i<args.length;i++){
			if(args[i] == null ) continue;
			if(types[i].getName().equals("com.bea.wli.sb.pipeline.RouterContext")){
				RouterContext ctx = (RouterContext)args[i];
				MessageContext mctx = (MessageContext)ctx.getMessageContext();
				messageId = mctx.getMessageId();
			
				logger.debug("Extracting Message Id :"+messageId);
			}
		}// for
		return messageId;
		
	}
	public String getServiceName(JoinPoint point){
		String proxyName = null;
		
		Object[] args = point.getArgs();
		Class[] types = ( (CodeSignature)point.getSignature()).getParameterTypes();
		
		if(args.length>0){
			// get RouterContext
			if(args[0]==null) return null;
			RouterContext rctx = (RouterContext)args[0];
			InternalPipelineContext pctx = rctx.getPipelineContext();
			
			String pipelineContext = pctx.toString();
			// Find string "serviceName=ProxyService" 
			// extract proxy name
			// until "nodeName="
			
			int start = pipelineContext.indexOf("serviceName=ProxyService");
			int end   = pipelineContext.indexOf("nodeName",start);
			
			//logger.debug("## PIPELINE Context :"+pipelineContext+" start :"+start+" end:"+end);
			proxyName = pipelineContext.substring(start+24,end);
			
		}
		return proxyName;		
	}
	public  void printParameters(JoinPoint point){
		Object[] args = point.getArgs();
		String[] names = ( (CodeSignature)point.getSignature()).getParameterNames();
		Class[] types = ( (CodeSignature)point.getSignature()).getParameterTypes();
		
		String className = point.getSignature().getDeclaringTypeName();
		String methodName = point.getSignature().getName();
		//logger.info("#### Class "+className+":"+methodName);
		for(int i=0;i<args.length;i++){
			if(args[i] == null ) continue;
			String arg = args[i].toString();
			if(arg.length() > 200) arg = arg.substring(0,199);
			//logger.info("\t"+i+"."+types[i].getName()+":"+names[i]+" ="+args[i]);
			logger.log(i+" "+types[i].getName()+":"+args[i]);
		}// for
		//Exception e=new Exception();
		//e.printStackTrace();
	}
	
	
}
